Object-oriented refactoring of active stress#578
Conversation
…forwarded to ionic model and active stress
| /// State variables for the model. | ||
| Array<double> states; |
There was a problem hiding this comment.
The state vector for the active stress model is stored into the ActiveStress instance itself, instead of at a more "global" scope as for the Xion state vector of IonicModel.
I think this is a slightly better design, and deals better with heterogeneous models across domain interfaces. Accordingly, I think this would be a good design to adopt for IonicModel too, although perhaps it's better to do it in a follow-up PR.
| /// Active tension coefficient along the fiber direction. | ||
| double eta_f; | ||
|
|
||
| /// Active tension coefficient along the sheet direction. | ||
| double eta_s; | ||
|
|
||
| /// Active tension coefficient along the sheet-normal direction. | ||
| double eta_n; |
There was a problem hiding this comment.
@dseyler for now these are domain-wide constant, same as in the old code. I think this class would be the perfect place to implement spatially-varying values for these coefficients in a way that makes that feature available to all concrete active stress models.
| /** | ||
| * @brief Nash-Panfilov active stress model. | ||
| * | ||
| * This class implements the Nash-Panfilov active stress model [1], with the | ||
| * modifications introduced by Goktepe and Kuhl [2]. | ||
| * | ||
| * The model equations are the following: | ||
| * @f[ \begin{aligned} | ||
| * \dv{\Tact}{t} &= \varepsilon(\calcium)( | ||
| * \eta_\text{T} (\calcium - \calcium_\text{rest}) - \Tact)\;, \\ | ||
| * \varepsilon(\calcium) &= | ||
| * \varepsilon_0 + (\varepsilon_i - \varepsilon_0) | ||
| * \exp(-\exp(-\xi_T (\calcium - \calcium_\text{crit})))\;, | ||
| * \end{aligned} @f] | ||
| * where @f$\eta_\text{T}@f$, @f$\calcium_\text{rest}@f$, | ||
| * @f$\calcium_\text{crit}@f$, @f$\varepsilon_0@f$, @f$\varepsilon_i@f$ and | ||
| * @f$\xi_T@f$ are user-defined model parameters. The function | ||
| * @f$\varepsilon(\calcium)@f$ is a sigmoidal-shaped calcium-dependent time | ||
| * constant (see Figure 3 in [2] for more details). | ||
| * | ||
| * @note The sensitivity of the model to calcium is controlled by the paramter | ||
| * @f$\eta_\text{T}@f$, which has the same units of active tension over calcium. | ||
| * Therefore, if the ionic model providing the calcium is phenomenological (see | ||
| * @ref IonicModel) and calcium is non-dimensional, this parameter may need to | ||
| * be rescaled as well. Similar considerations apply to @f$\xi_T@f$. | ||
| * | ||
| * **References**: | ||
| * 1. [Nash, Panfilov (2004)](https://doi.org/10.1016/j.pbiomolbio.2004.01.016) | ||
| * 2. [Goktepe, Kuhl (2009)](https://doi.org/10.1007/s00466-009-0434-z) | ||
| */ | ||
| class NashPanfilov : public ActiveStressODE { |
There was a problem hiding this comment.
Here there's a slight departure from the previous implementation.
In the old code, the Nash-Panfilov model was driven by potential if coupled to the Aliev-Panfilov or Bueno-Orovio models, and by calcium otherwise. I imagine this choice was mostly due to the phenomenological nature of AP and BO, where calcium is not really a variable. However, both models have a variable that resembles calcium transients more closely than the potential does, which they return through their get_calcium_index methods.
I chose to only feed the calcium-like variables into the active stress model, and not the potential, to keep the ActiveStress interface more lean and closer to what makes biological sense. This means that the behavior of Nash-Panfilov coupled to AP or BO will be slightly different (in my opinion slightly more correct).
Notice that, even before #540, the coupling code was effectively dead code, so there's no test case that is affected by this change in behavior.
| // Forward Euler time stepping. | ||
| Vector<double> f = getf(t, state, calcium, fiber_stretch, fiber_stretch_rate); | ||
|
|
||
| state.add(dt, f); |
There was a problem hiding this comment.
We might want to at least offer the option of a Runge-Kutta method, but it should be relatively straightforward to do that at a later stage.
| /** | ||
| * @brief Uniform and steady active stress model. | ||
| * | ||
| * Defines an active tension that is constant in space and time, i.e. | ||
| * @f[ | ||
| * \Tact(t, \calcium, \fiberstretch, \fiberstretchrate, \astressstate) = g\;, | ||
| * @f] | ||
| * where @f$g@f$ is a user-defined constant value. | ||
| */ | ||
| class UniformSteadyActiveStress : public ActiveStress { |
There was a problem hiding this comment.
@dseyler In my mind, unsteady prescribed active stress can be modeled after this class (of course with a bit of added complications to it, related e.g. to loading from file the time-dependent active tension profile, and possibly the activation times used to shift it).
I think once #577 is merged this should be rather easy.
| /// @brief Parameters for a generic active stress model. | ||
| /// | ||
| /// This class is meant to be inherited from to implement parameters for | ||
| /// specific active stress models. Derived classes will mostly have to call | ||
| /// add_parameter in their constructor to define the model-specific parameters. | ||
| /// | ||
| /// In the XML file, this class, and the classes derived from it, correspond to | ||
| /// the element <Model_name> within the <Active_stress> element, where | ||
| /// Model_name is the name of a concrete active stress model. | ||
| class ActiveStressModelParameters : public ParameterLists { |
There was a problem hiding this comment.
Similar to IonicModelParameters (original thread), this class might be implementing general-purpose stuff that would probably make more sense in ParameterList. I am still a bit confused about the parameter parsing logic, so I think this might be a sub-optimal solution.
I think similar problems are present in other classes in this file (e.g. there's a few classes redefining the xml_element_name member although they inherit it from ParameterLists), and overall parameter parsing might benefit from a bit of rationalization, but perhaps this is not the right PR for that.
| // @todo[michelebucelli] The active tension here was kept to zero, | ||
| // so I replicated the same for fibers, sheets and normals. This | ||
| // might not be the correct thing to do. | ||
| mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, | ||
| fN, /* ya_f = */ 0.0, /* ya_s = */ 0.0, | ||
| /* ya_n = */ 0.0, S, Dm, Ja); |
There was a problem hiding this comment.
Does anyone have any insight about this?
Maybe the correct interpretation is that the active stress is kept to zero because this code aims at computing only the passive part of thes stress, but I am not sure about it.
Partially addresses #519.
Current situation
Release Notes
Many of the implementation choices here are modeled after the implementation of
IonicModelin #540. Here, I took some further steps towards encapsulation which might be ported toIonicModelas well, although maybe in a separate PR. I'll add some comments to the code about this.ActiveStressorActiveStressODE.--- config: class: hideEmptyMembersBox: true --- classDiagram ActiveStress <|-- ActiveStressODE ActiveStressODE <|-- NashPanfilov ActiveStress <|-- UniformSteadyActiveStress dmnType *-- ActiveStress ActiveStressFactory .. ActiveStress : instantiates Factory~BaseClass~ <|.. ActiveStressFactory : BaseClass=ActiveStressA new abstract
Factoryclass is introduced, by templating theIonicModelFactory; aliases are provided for bothFactory<IonicModel>andFactory<ActiveStress>.The
ActiveStressclass and derived classes manage their own XML parameters through the abstract classActiveStressParametersand the methodsget_parameters,read_parameters,distribute_parameters, so that the addition of model parameters can be localized to the source files where the model is defined.A (shared) pointer to
ActiveStressis stored indmnType, to allow different active stress models in different domains.Time stepping of
ActiveStressinstances is managed byIntegrator::predictor. After advancing one time step, the active tension along the three principal directions is stored in the vectorCepMod::cem.Ya_{f,s,n}, whose entries are then passed on to the constitutive law routines.The XML section for a
structdomain now can optionally contain the following tag:If present, the tag enables active stress through the
ActiveStressclass. If the active stress model is calcium-based, and no electrophysiology equation is present, the calcium will be kept to zero.Documentation
New additions are documented via Doxygen comments. Documentation for
ActiveStressandActiveStressODEalso includes a brief list of steps needed to implement a new model.Testing
Existing automatic tests pass with the new implementation, although they do not cover the newly added features.
I have tested this on a basic electromechanics example based on

cases/cep/slab_domains. I attach below a preview of the simulation.I plan to turn this into an integration test (running one timestep is reasonably fast).
TODO before marking as ready
Code of Conduct & Contributing Guidelines